<?php
namespace Console\OBD;

use Commons\App;
use Swoole\Server;

class Event
{
    protected $_executive = [
        '0022' => 'getAppVersion',
        '0033' => 'setOrQueryFacilityId',
        '0004' => 'restoreFactorySetting',
        // '008A' => 'callThePolice',
        '0088' => 'callThePolice',
        //GPS 0x20 GPS命令
        '2001' => 'setGpsInfoPassInterval',//设置GPS位置信息上传通道和间隔
        '2002' => 'requestGpsLocation', //请求GPS位置信息
        '2081' => 'queryGpsInfoPassInterval',//查询GPS位置信息上传通道和间隔
        '2082' => 'UploadGpsLocation', //上传GPS位置信息
        '2003' => 'setGpsAndObdUploadInfo', //设置混合信息上传间隔
        '2083' => 'queryGpsAndObdUploadInfo',//混合信息上传间隔
        '2004' => 'requestGpsAndObdInfo',//请求GPS+OBD混合信息
        '2084' => 'uploadGpsAndObdInfo',//上传GPS+OBD混合信息

        //OBD 0x30 OBD命令
        '3003' => 'setCarModel', //设置车型
        '3083' => 'queryCarModel', //查询车型
        '3004' => 'queryCarVin',  //查询车辆VI码
        '3006' => '',
        '3008' => 'uploadRoute',
        '3089' => 'fireExitReport',
        '3088' => 'uploadTripReport'

    ];

    protected $_events = [
        '00' => 'GeneralEvent',
        '10' => 'GSMEvent',
        '20' => 'GPSEvent',
        '30' => 'OBDEvent',
        '40' => 'UpgradeEvent',
        '50' => 'PeripheralEvent',
    ];

    public function __construct()
    {
    }

    /**
     * @param Server $server
     * @param int    $fd
     * @param int    $from_id
     * @param string $data
     *
     * @throws \Exception
     */
    public function dispatcher(Server $server, int $fd, int $from_id, string $data)
    {
        $str     = bin2hex(substr($data, 1, -1));
        $search  = ['3d14', '3d15', '3d00'];
        $replace = ['29', '28', '00'];
        $str     = str_replace($search, $replace, $str);
        $str     = hex2bin($str);
        //计算cmd内容长度  设备Id 6 命令字 2 命令长度2 开始符1 结束符为1 校验和1
        $cmdContentLen = strlen($str) - (6 + 2 + 2 + 1);
        $message       = unpack(sprintf('H12deviceId/H4cmd/H4cmdLen/H%scmdContent/H2xhr', 2 * $cmdContentLen), $str);
        $deviceId      = $message['deviceId'];
        $cmd           = $message['cmd'];
        $cmdLen        = $message['cmdLen'];
        $content       = $message['cmdContent'];
        $this->timing($server, $fd, $deviceId);
        //        $xhr = $message['xhr'];
        //
        //        //拼接校验字符串
        //        $xhrStr = str_split($deviceId . $cmd . $cmdLen . $content, 2);

        //        if (!$this->checkXhrSum($xhrStr, $xhr)) {
        //            App::Logger()->error(sprintf('设备ID为[%s]数据包校验失败,时间为[%s]', $deviceId, date('Ymd H:i:s')));
        //            return false;
        //        }
        App::Logger()->debug(sprintf('设备ID为[%s]接收了[%s]命令长度为[%s]命令内容为[%s]接收时间为[%s]', $deviceId, $cmd, $cmdLen, $content, date('Ymd H:i:s')));
        $method    = $this->_executive[$cmd];
        $className = "\\Console\\OBD\\Events\\" . $this->_events[str_split($cmd, 2)[0]];
        $class     = new $className();
        if (method_exists($class, $method)) {
            call_user_func_array([$class, $method], [$server, $fd, $from_id, $message]);
        }
    }

    /**
     * 校验异或和
     *
     * @param  array $xhrStr
     * @param string $check
     *
     * @return bool
     */
    private function checkXhrSum($xhrStr, $check = '')
    {
        if (!is_array($xhrStr)) {
            return false;
        }
        $cnt = count($xhrStr);

        $checksum = 0;

        for ($i = 0; $i < $cnt; $i++) {
            $part     = $xhrStr[$i];
            $nr       = hexdec($part);
            $checksum ^= $nr;
        }
        $checksum &= 0xff;

        $check = hexdec($check);
        echo $checksum;
        if ($checksum == $check) {
            return true;
        }
        return false;
    }

    /**
     * 发送心跳包失败就断开连接
     *
     * @param Server $server
     * @param int    $fd
     */
    public function heartbeat(Server $server, int $fd)
    {
        if (ServerTask::isTcpClient($fd, $server)) {
            if (!$server->send($fd, ' ')) {
                $server->close($fd);
            }
        }
    }

    /**
     * 授时
     *
     * @param Server $server
     * @param int    $fd
     * @param        $deviceId
     *
     * @throws \Exception
     */
    public function timing(Server $server, int $fd, $deviceId)
    {
        if ((ServerTask::redis()->zScore(ServerTask::$connectionSet, $fd)) == $fd) {
            ServerTask::redis()->zRem(ServerTask::$connectionSet, $fd);
            ServerTask::redis()->zAdd(ServerTask::$connectionSet, $fd, $deviceId);
            $deviceId  = implode(' ', str_split($deviceId, 2));
            $cmdLength = '00 0C 00 07 01';
            $date      = date('Y-m-d-H-i-s');
            $date      = explode('-', $date);
            $date[0]   = mb_substr($date[0], -2, 2);
            $dateline  = array_reverse(array_slice($date, 0, 3));
            $time      = array_slice($date, 3, 3);
            $string    = implode(' ', $dateline) . ' ' . implode(' ', $time);
            $string    = $deviceId . ' ' . $cmdLength . ' ' . $string;
            $checksum  = 0x00;
            $parts     = explode(' ', $string);
            for ($i = 0; $i < count($parts); $i++) {
                $part     = $parts[$i];
                $nr       = hexdec($part);
                $checksum ^= $nr;
            }
            $checksum  &= 0xff;
            $cmdStrArr = explode(' ', $string);
            for ($i = 0; $i < count($cmdStrArr); $i++) {
                $cmdHex[] = chr(hexdec($cmdStrArr[$i]));
            }
            $cmdHex[] = chr($checksum);
            //转换为字符串
            $cmdHex = implode($cmdHex);
            $data   = chr(hexdec('28')) . $cmdHex . chr(hexdec('29'));
            App::Logger()->debug(sprintf("设备ID为[%s]授时,时间为[%s]", $deviceId, date('Y-m-d-H:i:s')));
            $server->send($fd, $data);
        }
    }


}
